package event_lite

import (
	"context"
	"encoding/json"
	"fmt"
	"net"
	"strconv"
)

type EventHandler struct {
	Ctx              context.Context
	ExitFunc         context.CancelFunc
	Port             int
	subscribes       map[string]func(event Event)
	hasExit          bool
	conn             *net.UDPConn
	ServiceProviders []*ServiceProvider
}

func New(ctx context.Context) (instance EventLite) {
	eh := &EventHandler{}
	c, cancel := context.WithCancel(ctx)
	eh.Ctx = c
	eh.ExitFunc = cancel
	eh.subscribes = make(map[string]func(event Event))
	instance = eh
	return
}

func (h *EventHandler) Stop() {
	if h.ExitFunc != nil {
		h.ExitFunc()
	}
}

func (h *EventHandler) SendMessage(event Event, target *ServiceProvider) (err error) {
	bs, err := json.Marshal(event)
	if err != nil {
		return
	}
	_, err = target.Conn.Write(bs)
	return
}

func (h *EventHandler) Subscribe(topic string, handler func(e Event)) {
	if len(topic) == 0 || handler == nil {
		return
	}
	h.subscribes[topic] = handler
}

func (h *EventHandler) StartServer(port int) (err error) {
	h.Port = port
	addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", port))
	h.conn, err = net.ListenUDP("udp", addr)
	if err != nil {
		return
	}
	go h.loop()
	return
}

func (h *EventHandler) CreateService(opt ...ServiceOptFunc) (svr *ServiceProvider, err error) {

	option := &ServiceOpt{}
	for _, o := range opt {
		o(option)
	}
	addr, err := net.ResolveUDPAddr("udp", option.Host+":"+strconv.Itoa(option.Port))
	if err != nil {
		return
	}
	conn, err := net.DialUDP("udp", nil, addr)
	if err != nil {
		return
	}
	svr = &ServiceProvider{conn}
	h.ServiceProviders = append(h.ServiceProviders, svr)
	return
}

func (h *EventHandler) loop() {
	for {
		if h.hasExit {
			break
		}
		select {
		case <-h.Ctx.Done():
			h.hasExit = true
			break
		default:
			e := h._loop()
			if e != nil {
				fmt.Println(e)
			}
		}
		if h.hasExit {
			fmt.Println("exit subscribe process")
			break
		}
	}
}

func (h *EventHandler) _loop() (err error) {
	if h.hasExit {
		return
	}
	if h.conn == nil {
		return
	}
	var buf = make([]byte, 1024)
	n, addr, err := h.conn.ReadFromUDP(buf)

	if err != nil {
		return
	}
	fmt.Println(addr)
	if n == 0 {
		return // TODO
	}
	if n > 0 {
		data := buf[:n]
		fmt.Println("<<" + string(data))
		var event Event
		err = json.Unmarshal(data, &event)
		if err != nil {
			return
		}

		h.onEvent(event)
	}
	return
}

func (h *EventHandler) onEvent(event Event) {
	if len(h.subscribes) == 0 {
		return
	}
	handler, ok := h.subscribes[event.Channel]
	if !ok { // 未订阅,忽略
		return
	} else {
		if handler != nil {
			handler(event)
		}
	}
}

func (h *EventHandler) release() {
	if !h.hasExit {
		return
	}

	if len(h.ServiceProviders) > 0 {
		for _, sp := range h.ServiceProviders {
			if sp == nil {
				continue
			}
			sp.Close()
		}
	}

	if len(h.subscribes) > 0 {
		clear(h.subscribes)
	}
}
